This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

Data visualisation

library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages -------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats

Where can I find useful packages?

Where can I find how to use packages

  • Reference manual on CRAN
  • Vignettes
  • ?
  • Demos
# List vignettes from all *attached* packages
vignette(all = FALSE)
# List vignettes from all *installed* packages (can take a long time!):
vignette(all = TRUE)
# find vignettes of "ggplot2"
vignette(package = "ggplot2")
# view vignette "ggplot2-specs"  
vignette("ggplot2-specs")

now look for more information on ggplot

?ggplot2
demo()          # find demos for attached packages
demo(graphics)  # A show of some of R's graphics capabilities, run in console

lets look at the some data

note that the pipe can be run in parts (short cut Ctrl+Shift+M, CMD+SHIFT+M )

mpg  %>% select(displ, cty, hwy, year)  %>% plot()

plot(select(mpg,displ,cty,hwy,year))

Creating a ggplot

ggplot is part of the tidyverse and a widely used package to work with graphics note for ggplot there is “+” to combine commands, in contrast to “% > %” which is the pipe operator for commands outside ggplot

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy))

Create a ggplot with color = class

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color = class))

Create a ggplot with size = class

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, size = class))

Create a ggplot with alpha = class

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, alpha = class))

Create a ggplot with shape = class

note there are only 6 different shapes, therefore “suv” has no shape and is not displayed

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, shape = class))

Create plot where property of geom is set manually

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy), color = "blue")

Recap

  • Where would you check for packages?
  • Where would you look on how to use packages?
  • When would you use size as function of a value in a plot?

Facets

If there is a variable value which separates data it can be used to create multiple plots rather than multiple lines in one plot.

facet_wrap

facet_wrap wraps a 1d sequence of panels into 2d

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) + 
  facet_wrap(~ class, nrow = 2)

facet_grid

facet_grid forms a matrix of panels defined by row and column facetting variables.

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) + 
  facet_grid(drv ~ cyl)

Geometic objects

different ways to present the same data

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) 
ggplot(data = mpg) + 
  geom_smooth(mapping = aes(x = displ, y = hwy))
ggplot(data = mpg) + 
  geom_smooth(mapping = aes(x = displ, y = hwy, linetype = drv, color = drv))

avoid the legend

ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy, group = drv))

display several geoms in same plot

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) +
  geom_smooth(mapping = aes(x = displ, y = hwy))

don’t repeat code

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(mapping = aes(color = class)) + 
  geom_smooth()

use only subset of data for geom

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(mapping = aes(color = class)) + 
  geom_smooth(data = filter(mpg, class == "subcompact"), se = FALSE)

lost in all the options?

CHEATSHEETS are at your fingertips under HELP menu of RStudio IDE or https://www.rstudio.com/resources/cheatsheets/

Statistical transformations

bar plot for discrete x-data

ggplot(data = diamonds) + 
  geom_bar(mapping = aes(x = cut))

box plot for discrete x- and continuous y-data

ggplot(data = diamonds) + 
  geom_boxplot(mapping = aes(x = cut, y = price))

Violin plot for discrete x- and continuous y-data

gives good impression of distribution

ggplot(data = diamonds) + 
  geom_violin(mapping = aes(x = cut, y = price, color = cut))

Histogram

A histogram is a graphical representation of the distribution of numerical data.

https://de.wikipedia.org/wiki/Histogramm

ggplot(diamonds, aes(carat)) +
  geom_histogram()
# set binwidth
ggplot(diamonds, aes(carat)) +
  geom_histogram(binwidth = 0.01)
# set number of bins
ggplot(diamonds, aes(carat)) +
  geom_histogram(bins = 200)

use geom_freqpoly for easier comparison

# Rather than stacking histograms, it's easier to compare frequency
# polygons
ggplot(diamonds, aes(price, fill = cut)) +
  geom_histogram(binwidth = 500)
ggplot(diamonds, aes(price, colour = cut)) +
  geom_freqpoly(binwidth = 500)

work with densities, means each curve has area of one

# To make it easier to compare distributions with very different counts,
# put density on the y axis instead of the default count
ggplot(diamonds, aes(price, ..density.., colour = cut)) +
  geom_freqpoly(binwidth = 500)

Empirical Cumulative Distribution Function (ECDF)

The empirical distribution function estimates the cumulative distribution function underlying of the points in the sample and converges with probability 1

https://de.wikipedia.org/wiki/Empirische_Verteilungsfunktion

df <- data.frame(x = rnorm(10000))
ggplot(df, aes(x)) +
  geom_histogram()
ggplot(df, aes(x)) + stat_ecdf(geom = "step")

p  <- ggplot(df, aes(x)) + stat_ecdf()
pg <- ggplot_build(p)$data[[1]]
ggplot(pg, aes(x = x, y = 1-y )) + geom_step() + scale_y_log10() 

Recap

  • Which geom seems useful for you?
  • Can you think of a use case for a facet plot?

one more source for information https://www.rdocumentation.org

Data wrangling

filter rows

filter all rows where month == 1 and day ==1, multiple filter conditions are separated by “,”

filter(flights, month == 1, day == 1)

store all x-mas flights

note, if you wrap the expression in () then the result will be displayed even when the result is assigned to a variable

(xmas_flights <- filter(flights, month == 12, day == 24))

boolean operators work as well

filter(flights, month == 11 | month == 12)

the following expressions give the same result

filter(flights, !(arr_delay > 120 | dep_delay > 120))
filter(flights, arr_delay <= 120, dep_delay <= 120)

Arrange rows with arrange()

arrange(flights, year, month, day)

select columns with select()

also an easy way to bring columns in a specific order

select(flights, year, month, day)

select all but a range of columns

select(flights, -(year:day))

more can be found in the cheatsheet

Add new variables with mutate()

note the %>% operator

select(flights, 
  year:day, 
  ends_with("delay"), 
  distance, 
  air_time) %>% 
mutate(
  gain = arr_delay - dep_delay,
  speed = distance / air_time * 60,
  hours = air_time / 60,
  gain_per_hour = gain / hours) %>% 
  select(-c(month, day, speed))

if you only want to keep the new columns use “transmute()”

select(flights, 
  year:day, 
  ends_with("delay"), 
  distance, 
  air_time) %>% 
transmute(
  gain = arr_delay - dep_delay,
  speed = distance / air_time * 60,
  hours = air_time / 60,
  gain_per_hour = gain / hours) 

Grouped summaries with summarise()

the mean of all depature delays

summarise(flights, delay = mean(dep_delay, na.rm = TRUE))

# na.rm a logical value indicating whether NA values should be stripped before the computation proceeds.

find pattern of delays during the year

Find planes with high delays

not_cancelled <- flights %>% 
  filter(!is.na(arr_delay))
not_cancelled %>% 
  group_by(tailnum) %>% 
  summarise(
    delay = mean(arr_delay)
  ) %>%
ggplot( mapping = aes(x = delay)) + 
  geom_freqpoly(binwidth = 10)

there seems a few planes with very high mean delay. Lets look closer into the issue

delays <- not_cancelled %>% 
  group_by(tailnum) %>% 
  summarise(
    delay = mean(arr_delay, na.rm = TRUE),
    n = n()
  )
ggplot(data = delays, mapping = aes(x = n, y = delay)) + 
  geom_point(alpha = 1/10)

the high delays are for tailnum wiht limited number of flight. Lets choose only tailnums where at least 25 flights are recorded

delays %>% 
  filter(n > 25) %>% 
  ggplot(mapping = aes(x = n, y = delay)) + 
    geom_point(alpha = 1/10)

what if we want to select the points under consideration not via a limit but from a plot? Use Shiny Gadgets

library(shiny)
library(miniUI)
ggbrush <- function(data, xvar, yvar) {
  
  ui <- miniPage(
    gadgetTitleBar("Drag to select points"),
    miniContentPanel(
      # The brush="brush" argument means we can listen for
      # brush events on the plot using input$brush.
      plotOutput("plot", height = "100%", brush = "brush")
    )
  )
  
  server <- function(input, output, session) {
    
    # Render the plot
    output$plot <- renderPlot({
      # Plot the data with x/y vars indicated by the caller.
      ggplot(data, aes_string(xvar, yvar)) + geom_point()
    })
    
    # Handle the Done button being pressed.
    observeEvent(input$done, {
      # Return the brushed points. See ?shiny::brushedPoints.
      stopApp(brushedPoints(data, input$brush, allRows = TRUE))
    })
  }
  
  runGadget(ui, server)
}
# pick_points(mtcars, ~wt, ~mpg)
brushed_points <- ggbrush(delays, "n", "delay")

Listening on http://127.0.0.1:4198
brushed_points   %>% ggplot(mapping = aes(x = n, y = delay, color = selected_)) + 
    geom_point(alpha = 1/10)

brushed_points   %>% filter(selected_ ==TRUE)  %>%  ggplot(mapping = aes(x = n, y = delay, color = selected_)) + 
    geom_point(alpha = 1/10)

now a few more things we need for the EuropeLeagueTransfers.Rmd

left_join

the data set nycflights13 has four tibbles (dataframes)

  • airlines
  • airports
  • planes
  • weather
 airlines
 airports
 planes
 weather

lets find out which manufacturer has the highest delays

first we need to join flights with planes

lets find out which airline has the highest delays

first we need to join flights with planes

flight_airlines <- left_join(flights, airlines)
Joining, by = "carrier"
flight_airlines %>% group_by(name) %>% summarise(delay_per_flight = sum(arr_delay, na.rm = TRUE)/ n(),number_of_flights = n()) %>% arrange(desc(delay_per_flight))

long and wide data.frames

for some operations the tidy wide format is not suitable as input to an operation, then a “long” version of the data.frame can be generated using the “melt” command. A further example will be shown in EuropeLeagueTransfers.Rmd and further information on the topic can be found at http://seananderson.ca/2013/10/19/reshape.html

library(reshape2)
names(airquality) <- tolower(names(airquality))
aqm <- melt(airquality, id=c("month", "day"),
  variable.name = "climate_variable", 
  value.name = "climate_value")
airquality
aqm
acast_result[22,5,]  # arrays are accessed 
  ozone solar.r    wind    temp 
   23.0    14.0     9.2    71.0 

last thing we need for EuropeLeagueTransfers.Rmd

grepl returns a logic vector given an expression

letters
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v"
[23] "w" "x" "y" "z"

Lets dive into some code

EuropeLeagueTransfers.Rmd

LS0tCnRpdGxlOiAiUiBLZW5udG5pc3NlIFZIUyAyMDE3LzEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiAKClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgcGxhY2luZyB5b3VyIGN1cnNvciBpbnNpZGUgaXQgYW5kIHByZXNzaW5nICpDbWQrU2hpZnQrRW50ZXIqLiAKCgojIERhdGEgdmlzdWFsaXNhdGlvbgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCnRpZHl2ZXJzZV9wYWNrYWdlcygpICAjIHdoaWNoIHBhY2thZ2VzIGFyZSBpbiB0aWR5dmVyc2UKYGBgCgojIyBXaGVyZSBjYW4gSSBmaW5kIHVzZWZ1bCBwYWNrYWdlcz8KCi0gQ1JBTiB0YXNrIGxpc3QgIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnCi0gci1ibG9nZ2VycyBzZWFyY2ggaHR0cDovL3d3dy5yLWJsb2dnZXJzLmNvbSAKCiMjIFdoZXJlIGNhbiBJIGZpbmQgaG93IHRvIHVzZSBwYWNrYWdlcwoKLSBSZWZlcmVuY2UgbWFudWFsIG9uIENSQU4KLSBWaWduZXR0ZXMKLSA/Ci0gRGVtb3MKCgoKCmBgYHtyfQojIExpc3QgdmlnbmV0dGVzIGZyb20gYWxsICphdHRhY2hlZCogcGFja2FnZXMKdmlnbmV0dGUoYWxsID0gRkFMU0UpCiMgTGlzdCB2aWduZXR0ZXMgZnJvbSBhbGwgKmluc3RhbGxlZCogcGFja2FnZXMgKGNhbiB0YWtlIGEgbG9uZyB0aW1lISk6CnZpZ25ldHRlKGFsbCA9IFRSVUUpCiMgZmluZCB2aWduZXR0ZXMgb2YgImdncGxvdDIiCnZpZ25ldHRlKHBhY2thZ2UgPSAiZ2dwbG90MiIpCiMgdmlldyB2aWduZXR0ZSAiZ2dwbG90Mi1zcGVjcyIgIAp2aWduZXR0ZSgiZ2dwbG90Mi1zcGVjcyIpCmBgYAoKCm5vdyBsb29rIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGdncGxvdAoKYGBge3J9Cj9nZ3Bsb3QyCmRlbW8oKSAgICAgICAgICAjIGZpbmQgZGVtb3MgZm9yIGF0dGFjaGVkIHBhY2thZ2VzCmRlbW8oZ3JhcGhpY3MpICAjIEEgc2hvdyBvZiBzb21lIG9mIFIncyBncmFwaGljcyBjYXBhYmlsaXRpZXMsIHJ1biBpbiBjb25zb2xlCgpgYGAKCgojIyBsZXRzIGxvb2sgYXQgdGhlIHNvbWUgZGF0YQoKbm90ZSB0aGF0IHRoZSBwaXBlIGNhbiBiZSBydW4gaW4gcGFydHMgKHNob3J0IGN1dCBDdHJsK1NoaWZ0K00sIENNRCtTSElGVCtNICkKCmBgYHtyfQptcGcgICU+JSBzZWxlY3QoZGlzcGwsIGN0eSwgaHd5LCB5ZWFyKSAgJT4lIHBsb3QoKQoKcGxvdChzZWxlY3QobXBnLGRpc3BsLGN0eSxod3kseWVhcikpCmBgYAoKCgojIyBDcmVhdGluZyBhIGdncGxvdAoKZ2dwbG90IGlzIHBhcnQgb2YgdGhlIHRpZHl2ZXJzZSBhbmQgYSB3aWRlbHkgdXNlZCBwYWNrYWdlIHRvIHdvcmsgd2l0aCBncmFwaGljcyAKKipub3RlKiogZm9yIGdncGxvdCB0aGVyZSBpcyAiKyIgdG8gY29tYmluZSBjb21tYW5kcywgaW4gY29udHJhc3QgdG8gIiUgPiAlIiB3aGljaCBpcyB0aGUgcGlwZSBvcGVyYXRvciBmb3IgY29tbWFuZHMgb3V0c2lkZSBnZ3Bsb3QKCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpCmBgYAoKCiMjIENyZWF0ZSBhIGdncGxvdCB3aXRoIGNvbG9yID0gY2xhc3MKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGNsYXNzKSkKYGBgCgojIyBDcmVhdGUgYSBnZ3Bsb3Qgd2l0aCBzaXplID0gY2xhc3MKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBzaXplID0gY2xhc3MpKQpgYGAKCiMjIENyZWF0ZSBhIGdncGxvdCB3aXRoIGFscGhhID0gY2xhc3MKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBhbHBoYSA9IGNsYXNzKSkKYGBgCgojIyBDcmVhdGUgYSBnZ3Bsb3Qgd2l0aCBzaGFwZSA9IGNsYXNzCgoqKm5vdGUqKiB0aGVyZSBhcmUgb25seSA2IGRpZmZlcmVudCBzaGFwZXMsIHRoZXJlZm9yZSAic3V2IiBoYXMgbm8gc2hhcGUgYW5kIGlzIG5vdCBkaXNwbGF5ZWQKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBzaGFwZSA9IGNsYXNzKSkKYGBgCgoKCiMjIENyZWF0ZSBwbG90IHdoZXJlIHByb3BlcnR5IG9mIGdlb20gaXMgc2V0IG1hbnVhbGx5CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSksIGNvbG9yID0gImJsdWUiKQpgYGAKCiMjIFJlY2FwCi0gV2hlcmUgd291bGQgeW91IGNoZWNrIGZvciBwYWNrYWdlcz8KLSBXaGVyZSB3b3VsZCB5b3UgbG9vayBvbiBob3cgdG8gdXNlIHBhY2thZ2VzPwotIFdoZW4gd291bGQgeW91IHVzZSBzaXplIGFzIGZ1bmN0aW9uIG9mIGEgdmFsdWUgaW4gYSBwbG90PwoKCiMgRmFjZXRzCklmIHRoZXJlIGlzIGEgdmFyaWFibGUgdmFsdWUgd2hpY2ggc2VwYXJhdGVzIGRhdGEgaXQgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIG11bHRpcGxlIHBsb3RzIHJhdGhlciB0aGFuIG11bHRpcGxlIGxpbmVzIGluIG9uZSBwbG90LgoKIyMgZmFjZXRfd3JhcApmYWNldF93cmFwIHdyYXBzIGEgMWQgc2VxdWVuY2Ugb2YgcGFuZWxzIGludG8gMmQKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyAKICBmYWNldF93cmFwKH4gY2xhc3MsIG5yb3cgPSAyKQpgYGAKCgojIyBmYWNldF9ncmlkCmZhY2V0X2dyaWQgZm9ybXMgYSBtYXRyaXggb2YgcGFuZWxzIGRlZmluZWQgYnkgcm93IGFuZCBjb2x1bW4gZmFjZXR0aW5nIHZhcmlhYmxlcy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyAKICBmYWNldF9ncmlkKGRydiB+IGN5bCkKYGBgCgoKCiMgR2VvbWV0aWMgb2JqZWN0cwpkaWZmZXJlbnQgd2F5cyB0byBwcmVzZW50IHRoZSBzYW1lIGRhdGEKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgbGluZXR5cGUgPSBkcnYsIGNvbG9yID0gZHJ2KSkKYGBgCgojIyMgYXZvaWQgdGhlIGxlZ2VuZAoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBncm91cCA9IGRydikpCmBgYAoKCiMjIGRpc3BsYXkgc2V2ZXJhbCBnZW9tcyBpbiBzYW1lIHBsb3QKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKwogIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkKYGBgCgoKIyMgZG9uJ3QgcmVwZWF0IGNvZGUgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBjbGFzcykpICsgCiAgZ2VvbV9zbW9vdGgoKQpgYGAKCgojIyB1c2Ugb25seSBzdWJzZXQgb2YgZGF0YSBmb3IgZ2VvbQoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG9yID0gY2xhc3MpKSArIAogIGdlb21fc21vb3RoKGRhdGEgPSBmaWx0ZXIobXBnLCBjbGFzcyA9PSAic3ViY29tcGFjdCIpLCBzZSA9IEZBTFNFKQpgYGAKCgojIyBsb3N0IGluIGFsbCB0aGUgb3B0aW9ucz8KQ0hFQVRTSEVFVFMgYXJlIGF0IHlvdXIgZmluZ2VydGlwcyB1bmRlciBIRUxQIG1lbnUgb2YgUlN0dWRpbyBJREUgb3IKaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLyAKCgojIFN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9ucwoKIyMgYmFyIHBsb3QgZm9yIGRpc2NyZXRlIHgtZGF0YQpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKyAKICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjdXQpKQpgYGAKCiMjIGJveCBwbG90IGZvciBkaXNjcmV0ZSB4LSBhbmQgY29udGludW91cyB5LWRhdGEKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpCmBgYAoKCiMjIFZpb2xpbiBwbG90IGZvciBkaXNjcmV0ZSB4LSBhbmQgY29udGludW91cyB5LWRhdGEKZ2l2ZXMgZ29vZCBpbXByZXNzaW9uIG9mIGRpc3RyaWJ1dGlvbgoKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgCiAgZ2VvbV92aW9saW4obWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UsIGNvbG9yID0gY3V0KSkKYGBgCgojIyBIaXN0b2dyYW0KQSBoaXN0b2dyYW0gaXMgYSBncmFwaGljYWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiBudW1lcmljYWwgZGF0YS4KCmh0dHBzOi8vZGUud2lraXBlZGlhLm9yZy93aWtpL0hpc3RvZ3JhbW0KCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyhjYXJhdCkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCiMgc2V0IGJpbndpZHRoCmdncGxvdChkaWFtb25kcywgYWVzKGNhcmF0KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSkKIyBzZXQgbnVtYmVyIG9mIGJpbnMKZ2dwbG90KGRpYW1vbmRzLCBhZXMoY2FyYXQpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwMCkKYGBgCgojIyB1c2UgZ2VvbV9mcmVxcG9seSBmb3IgZWFzaWVyIGNvbXBhcmlzb24KCmBgYHtyfQojIFJhdGhlciB0aGFuIHN0YWNraW5nIGhpc3RvZ3JhbXMsIGl0J3MgZWFzaWVyIHRvIGNvbXBhcmUgZnJlcXVlbmN5CiMgcG9seWdvbnMKZ2dwbG90KGRpYW1vbmRzLCBhZXMocHJpY2UsIGZpbGwgPSBjdXQpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1MDApCmdncGxvdChkaWFtb25kcywgYWVzKHByaWNlLCBjb2xvdXIgPSBjdXQpKSArCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDUwMCkKYGBgCgoKd29yayB3aXRoIGRlbnNpdGllcywgbWVhbnMgZWFjaCBjdXJ2ZSBoYXMgYXJlYSBvZiBvbmUKCmBgYHtyfQojIFRvIG1ha2UgaXQgZWFzaWVyIHRvIGNvbXBhcmUgZGlzdHJpYnV0aW9ucyB3aXRoIHZlcnkgZGlmZmVyZW50IGNvdW50cywKIyBwdXQgZGVuc2l0eSBvbiB0aGUgeSBheGlzIGluc3RlYWQgb2YgdGhlIGRlZmF1bHQgY291bnQKZ2dwbG90KGRpYW1vbmRzLCBhZXMocHJpY2UsIC4uZGVuc2l0eS4uLCBjb2xvdXIgPSBjdXQpKSArCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDUwMCkKYGBgCgojIyBFbXBpcmljYWwgQ3VtdWxhdGl2ZSBEaXN0cmlidXRpb24gRnVuY3Rpb24gKEVDREYpCgpUaGUgZW1waXJpY2FsIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiBlc3RpbWF0ZXMgdGhlIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIHVuZGVybHlpbmcgb2YgdGhlIHBvaW50cyBpbiB0aGUgc2FtcGxlIGFuZCBjb252ZXJnZXMgd2l0aCBwcm9iYWJpbGl0eSAxCgpodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9FbXBpcmlzY2hlX1ZlcnRlaWx1bmdzZnVua3Rpb24KCgpgYGB7cn0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gcm5vcm0oMTAwMDApKQpnZ3Bsb3QoZGYsIGFlcyh4KSkgKwogIGdlb21faGlzdG9ncmFtKCkKZ2dwbG90KGRmLCBhZXMoeCkpICsgc3RhdF9lY2RmKGdlb20gPSAic3RlcCIpCgpwICA8LSBnZ3Bsb3QoZGYsIGFlcyh4KSkgKyBzdGF0X2VjZGYoKQpwZyA8LSBnZ3Bsb3RfYnVpbGQocCkkZGF0YVtbMV1dCmdncGxvdChwZywgYWVzKHggPSB4LCB5ID0gMS15ICkpICsgZ2VvbV9zdGVwKCkgKyBzY2FsZV95X2xvZzEwKCkgCgoKCmBgYAoKCiMjIFJlY2FwCgotIFdoaWNoIGdlb20gc2VlbXMgdXNlZnVsIGZvciB5b3U/Ci0gQ2FuIHlvdSB0aGluayBvZiBhIHVzZSBjYXNlIGZvciBhIGZhY2V0IHBsb3Q/CgpvbmUgbW9yZSBzb3VyY2UgZm9yIGluZm9ybWF0aW9uIGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZwoKIyAgRGF0YSB3cmFuZ2xpbmcKCmBgYHtyfQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKZmxpZ2h0cwpgYGAKCgojIyBmaWx0ZXIgcm93cwoKZmlsdGVyIGFsbCByb3dzIHdoZXJlIG1vbnRoID09IDEgYW5kIGRheSA9PTEsIG11bHRpcGxlIGZpbHRlciBjb25kaXRpb25zIGFyZSBzZXBhcmF0ZWQgYnkgIiwiCgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIG1vbnRoID09IDEsIGRheSA9PSAxKQpgYGAKCgojIyBzdG9yZSBhbGwgeC1tYXMgZmxpZ2h0cwoKbm90ZSwgaWYgeW91IHdyYXAgdGhlIGV4cHJlc3Npb24gaW4gKCkgdGhlbiB0aGUgcmVzdWx0IHdpbGwgYmUgZGlzcGxheWVkIGV2ZW4gd2hlbiB0aGUgcmVzdWx0IGlzIGFzc2lnbmVkIHRvIGEgdmFyaWFibGUKCmBgYHtyfQooeG1hc19mbGlnaHRzIDwtIGZpbHRlcihmbGlnaHRzLCBtb250aCA9PSAxMiwgZGF5ID09IDI0KSkKYGBgCgoKIyMgYm9vbGVhbiBvcGVyYXRvcnMgd29yayBhcyB3ZWxsCgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIG1vbnRoID09IDExIHwgbW9udGggPT0gMTIpCmBgYAoKCnRoZSBmb2xsb3dpbmcgZXhwcmVzc2lvbnMgZ2l2ZSB0aGUgc2FtZSByZXN1bHQKCgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsICEoYXJyX2RlbGF5ID4gMTIwIHwgZGVwX2RlbGF5ID4gMTIwKSkKZmlsdGVyKGZsaWdodHMsIGFycl9kZWxheSA8PSAxMjAsIGRlcF9kZWxheSA8PSAxMjApCmBgYAoKCiMjIEFycmFuZ2Ugcm93cyB3aXRoIGFycmFuZ2UoKQoKCmBgYHtyfQphcnJhbmdlKGZsaWdodHMsIHllYXIsIG1vbnRoLCBkYXkpCmBgYAoKIyMgc2VsZWN0IGNvbHVtbnMgd2l0aCBzZWxlY3QoKQphbHNvIGFuIGVhc3kgd2F5IHRvIGJyaW5nIGNvbHVtbnMgaW4gYSBzcGVjaWZpYyBvcmRlcgoKYGBge3J9CnNlbGVjdChmbGlnaHRzLCB5ZWFyLCBtb250aCwgZGF5KQpgYGAKc2VsZWN0IGFsbCBidXQgYSByYW5nZSBvZiBjb2x1bW5zCgpgYGB7cn0Kc2VsZWN0KGZsaWdodHMsIC0oeWVhcjpkYXkpKQpgYGAKCm1vcmUgY2FuIGJlIGZvdW5kIGluIHRoZSBjaGVhdHNoZWV0IAoKIyMgQWRkIG5ldyB2YXJpYWJsZXMgd2l0aCBtdXRhdGUoKQoKbm90ZSB0aGUgJT4lIG9wZXJhdG9yCgpgYGB7cn0Kc2VsZWN0KGZsaWdodHMsIAogIHllYXI6ZGF5LCAKICBlbmRzX3dpdGgoImRlbGF5IiksIAogIGRpc3RhbmNlLCAKICBhaXJfdGltZSkgJT4lIAptdXRhdGUoCiAgZ2FpbiA9IGFycl9kZWxheSAtIGRlcF9kZWxheSwKICBzcGVlZCA9IGRpc3RhbmNlIC8gYWlyX3RpbWUgKiA2MCwKICBob3VycyA9IGFpcl90aW1lIC8gNjAsCiAgZ2Fpbl9wZXJfaG91ciA9IGdhaW4gLyBob3VycykgJT4lIAogIHNlbGVjdCgtYyhtb250aCwgZGF5LCBzcGVlZCkpCmBgYAoKaWYgeW91IG9ubHkgd2FudCB0byBrZWVwIHRoZSBuZXcgY29sdW1ucyB1c2UgInRyYW5zbXV0ZSgpIgoKYGBge3J9CnNlbGVjdChmbGlnaHRzLCAKICB5ZWFyOmRheSwgCiAgZW5kc193aXRoKCJkZWxheSIpLCAKICBkaXN0YW5jZSwgCiAgYWlyX3RpbWUpICU+JSAKdHJhbnNtdXRlKAogIGdhaW4gPSBhcnJfZGVsYXkgLSBkZXBfZGVsYXksCiAgc3BlZWQgPSBkaXN0YW5jZSAvIGFpcl90aW1lICogNjAsCiAgaG91cnMgPSBhaXJfdGltZSAvIDYwLAogIGdhaW5fcGVyX2hvdXIgPSBnYWluIC8gaG91cnMpIApgYGAKCgojIyBHcm91cGVkIHN1bW1hcmllcyB3aXRoIHN1bW1hcmlzZSgpCgp0aGUgbWVhbiBvZiBhbGwgZGVwYXR1cmUgZGVsYXlzCgpgYGB7cn0Kc3VtbWFyaXNlKGZsaWdodHMsIGRlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVFJVRSkpCgojIG5hLnJtCWEgbG9naWNhbCB2YWx1ZSBpbmRpY2F0aW5nIHdoZXRoZXIgTkEgdmFsdWVzIHNob3VsZCBiZSBzdHJpcHBlZCBiZWZvcmUgdGhlIGNvbXB1dGF0aW9uIHByb2NlZWRzLgoKYGBgCgoKCmBgYHtyfQpieV9kYXkgPC0gZ3JvdXBfYnkoZmxpZ2h0cywgeWVhciwgbW9udGgsIGRheSkKc3VtbWFyaXNlKGJ5X2RheSwgZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkKYGBgCgpmaW5kIHBhdHRlcm4gb2YgZGVsYXlzIGR1cmluZyB0aGUgeWVhcgoKYGBge3J9CmJ5X2RheSA8LSBmbGlnaHRzICU+JSBncm91cF9ieSh5ZWFyLCBtb250aCkKc3VtbWFyaXNlKGJ5X2RheSwgZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lIGdncGxvdChhZXMoIHggPSBtb250aCwgeSA9IGRlbGF5LCBncm91cCA9IG1vbnRoKSkgKwogIGdlb21fY29sKCkKYGBgCgoKCiMjIEZpbmQgcGxhbmVzIHdpdGggaGlnaCBkZWxheXMKCmBgYHtyfQpub3RfY2FuY2VsbGVkIDwtIGZsaWdodHMgJT4lIAogIGZpbHRlcighaXMubmEoYXJyX2RlbGF5KSkKCm5vdF9jYW5jZWxsZWQgJT4lIAogIGdyb3VwX2J5KHRhaWxudW0pICU+JSAKICBzdW1tYXJpc2UoCiAgICBkZWxheSA9IG1lYW4oYXJyX2RlbGF5KQogICkgJT4lCmdncGxvdCggbWFwcGluZyA9IGFlcyh4ID0gZGVsYXkpKSArIAogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAxMCkKYGBgCgp0aGVyZSBzZWVtcyBhIGZldyBwbGFuZXMgd2l0aCB2ZXJ5IGhpZ2ggbWVhbiBkZWxheS4gTGV0cyBsb29rIGNsb3NlciBpbnRvIHRoZSBpc3N1ZQoKYGBge3J9CmRlbGF5cyA8LSBub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieSh0YWlsbnVtKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSwKICAgIG4gPSBuKCkKICApCgpnZ3Bsb3QoZGF0YSA9IGRlbGF5cywgbWFwcGluZyA9IGFlcyh4ID0gbiwgeSA9IGRlbGF5KSkgKyAKICBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkKYGBgCgoKdGhlIGhpZ2ggZGVsYXlzIGFyZSBmb3IgdGFpbG51bSB3aWh0IGxpbWl0ZWQgbnVtYmVyIG9mIGZsaWdodC4KTGV0cyBjaG9vc2Ugb25seSB0YWlsbnVtcyB3aGVyZSBhdCBsZWFzdCAyNSBmbGlnaHRzIGFyZSByZWNvcmRlZAoKYGBge3J9CmRlbGF5cyAlPiUgCiAgZmlsdGVyKG4gPiAyNSkgJT4lIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBuLCB5ID0gZGVsYXkpKSArIAogICAgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApCmBgYAoKd2hhdCBpZiB3ZSB3YW50IHRvIHNlbGVjdCB0aGUgcG9pbnRzIHVuZGVyIGNvbnNpZGVyYXRpb24gbm90IHZpYSBhIGxpbWl0IGJ1dCBmcm9tIGEgcGxvdD8gVXNlICoqU2hpbnkgR2FkZ2V0cyoqCgpgYGB7cn0KbGlicmFyeShzaGlueSkKbGlicmFyeShtaW5pVUkpCgpnZ2JydXNoIDwtIGZ1bmN0aW9uKGRhdGEsIHh2YXIsIHl2YXIpIHsKICAKICB1aSA8LSBtaW5pUGFnZSgKICAgIGdhZGdldFRpdGxlQmFyKCJEcmFnIHRvIHNlbGVjdCBwb2ludHMiKSwKICAgIG1pbmlDb250ZW50UGFuZWwoCiAgICAgICMgVGhlIGJydXNoPSJicnVzaCIgYXJndW1lbnQgbWVhbnMgd2UgY2FuIGxpc3RlbiBmb3IKICAgICAgIyBicnVzaCBldmVudHMgb24gdGhlIHBsb3QgdXNpbmcgaW5wdXQkYnJ1c2guCiAgICAgIHBsb3RPdXRwdXQoInBsb3QiLCBoZWlnaHQgPSAiMTAwJSIsIGJydXNoID0gImJydXNoIikKICAgICkKICApCiAgCiAgc2VydmVyIDwtIGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQsIHNlc3Npb24pIHsKICAgIAogICAgIyBSZW5kZXIgdGhlIHBsb3QKICAgIG91dHB1dCRwbG90IDwtIHJlbmRlclBsb3QoewogICAgICAjIFBsb3QgdGhlIGRhdGEgd2l0aCB4L3kgdmFycyBpbmRpY2F0ZWQgYnkgdGhlIGNhbGxlci4KICAgICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeHZhciwgeXZhcikpICsgZ2VvbV9wb2ludCgpCiAgICB9KQogICAgCiAgICAjIEhhbmRsZSB0aGUgRG9uZSBidXR0b24gYmVpbmcgcHJlc3NlZC4KICAgIG9ic2VydmVFdmVudChpbnB1dCRkb25lLCB7CiAgICAgICMgUmV0dXJuIHRoZSBicnVzaGVkIHBvaW50cy4gU2VlID9zaGlueTo6YnJ1c2hlZFBvaW50cy4KICAgICAgc3RvcEFwcChicnVzaGVkUG9pbnRzKGRhdGEsIGlucHV0JGJydXNoLCBhbGxSb3dzID0gVFJVRSkpCiAgICB9KQogIH0KICAKICBydW5HYWRnZXQodWksIHNlcnZlcikKfQojIHBpY2tfcG9pbnRzKG10Y2Fycywgfnd0LCB+bXBnKQpicnVzaGVkX3BvaW50cyA8LSBnZ2JydXNoKGRlbGF5cywgIm4iLCAiZGVsYXkiKQoKYnJ1c2hlZF9wb2ludHMgICAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IG4sIHkgPSBkZWxheSwgY29sb3IgPSBzZWxlY3RlZF8pKSArIAogICAgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApCgpicnVzaGVkX3BvaW50cyAgICU+JSBmaWx0ZXIoc2VsZWN0ZWRfID09VFJVRSkgICU+JSAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IG4sIHkgPSBkZWxheSwgY29sb3IgPSBzZWxlY3RlZF8pKSArIAogICAgZ2VvbV9wb2ludChhbHBoYSA9IDEvMykKCmBgYAoKCgojIyBub3cgYSBmZXcgbW9yZSB0aGluZ3Mgd2UgbmVlZCBmb3IgdGhlIEV1cm9wZUxlYWd1ZVRyYW5zZmVycy5SbWQKCiMjIyBsZWZ0X2pvaW4KCnRoZSBkYXRhIHNldCBueWNmbGlnaHRzMTMgaGFzIGZvdXIgdGliYmxlcyAoZGF0YWZyYW1lcykKCi0gYWlybGluZXMKLSBhaXJwb3J0cwotIHBsYW5lcwotIHdlYXRoZXIKCgpgYGB7cn0KIGFpcmxpbmVzCiBhaXJwb3J0cwogcGxhbmVzCiB3ZWF0aGVyCmBgYAoKCiMjIGZpbmQgdGhlIGxpbmtzIGJldHdlZW4gdGhlIGRhdGEuZnJhbWVzCgoKYGBge3J9CmxpYnJhcnkodmlzTmV0d29yaykKIyB0aGlzIGZ1bmN0aW9uIGNyZWF0ZXMgYSBkYXRhLmZyYW1lIHdpdGggdGhlIG5hbWUgb2YgdGhlIGRhdGEuZnJhbWUgYW5kIHRoZSBuYW1lcyBvZiB0aGUgY29sdW1ucyBvZiB0aGF0IGRhdGEuZnJhbWUKY3JlYXRlX2RmX29mX25hbWVzID0gZnVuY3Rpb24oZGYsIG5hbWUpewogIGRhdGEuZnJhbWUoZnJvbSA9IG5hbWUsIHRvID0gbmFtZXMoZGYpKQp9CgojIGNyZWF0ZSBhIG5hbWVzIGxpc3Qgb2YgdGhlIGRhdGEuZnJhbWVzCmEgPC0gbGlzdChmbGlnaHRzID0gZmxpZ2h0cyxhaXJsaW5lcyA9IGFpcmxpbmVzLCBhaXJwb3J0cyA9IGFpcnBvcnRzLCB3ZWF0aGVyID0gd2VhdGhlciwKICAgICAgICAgIHBsYW5lcyA9IHBsYW5lcykgCiMgYW5kIG1hcCB0aGVtIHRvIGJ1aWxkIG9uZSBkYXRhLmZyYW1lIHdpdGggdHdvIGNvbHVtbnMKIyAtIGZyb20gY29udGFpbnMgYWxsICBkYXRhLmZyYW1lIG5hbWVzCiMgLSB0byAgY29udGFpbnMgYWxsIGNvbHVtbiBuYW1lcwplZGdlIDwtIG1hcDJfZGYoYSxuYW1lcyhhKSwgY3JlYXRlX2RmX29mX25hbWVzKQoKIyBjcmVhdGUgYSB2aXNOZXR3b3JrCgpub2Rlc0Zyb20gPC0gIGVkZ2UgJT4lIGNiaW5kKHVubGlzdCguJGZyb20pLCJUYWJsZSIpICU+JSBzZWxlY3QoMyw0KSAlPiUgZGF0YS5mcmFtZSAgCm5vZGVzVG8gPC0gIGVkZ2UgJT4lIGNiaW5kKHVubGlzdCguJHRvKSwiQXR0cmlidXRlIikgJT4lIHNlbGVjdCgzLDQpICU+JSBkYXRhLmZyYW1lIAoKbmFtZXMobm9kZXNGcm9tKSA8LSBjKCJpZCIsICJncm91cCIpCm5hbWVzKG5vZGVzVG8pIDwtIGMoImlkIiwgImdyb3VwIikKCm5vZGVzIDwtIHJiaW5kKG5vZGVzRnJvbSxub2Rlc1RvKSAlPiUgdW5pcXVlKCkgCm5vZGVzJGlkIDwtIGFzLmNoYXJhY3Rlcigobm9kZXMkaWQpKSAgCm5vZGVzIDwtIG5vZGVzICU+JSB1bmlxdWUoKSAlPiUgYXJyYW5nZShpZCkKdmlzTmV0d29yayhub2RlcywgZWRnZSklPiUKICB2aXNPcHRpb25zKGhpZ2hsaWdodE5lYXJlc3QgPSBsaXN0KGVuYWJsZWQgPSBUUlVFLCBkZWdyZWUgPSAyKSwgbm9kZXNJZFNlbGVjdGlvbiA9IFRSVUUpICU+JQogIHZpc0VkZ2VzKGFycm93cyA9ICJ0byIpICU+JSAgCiAgdmlzR3JvdXBzKGdyb3VwbmFtZSA9ICJUYWJsZSIsICAgICBzaGFwZSA9ICJpY29uIiwgaWNvbiA9IGxpc3QoY29kZSA9ICJmMTE0IiwgY29sb3IgPSAiZ3JlZW4iLHNpemUgPSA3NSkpICU+JQogIHZpc0dyb3Vwcyhncm91cG5hbWUgPSAiQXR0cmlidXRlIiwgc2hhcGUgPSAiaWNvbiIsIGljb24gPSBsaXN0KGNvZGUgPSAiZjExNSIsIGNvbG9yID0gImxpZ2h0Z3JlZW4iLCBzaXplID0gNDUpKSAlPiUKICBhZGRGb250QXdlc29tZSgpIAojIGxpc3Qgb2YgaWNvbnMgaHR0cDovL2FzdHJvbmF1dHdlYi5jby9zbmlwcGV0L2ZvbnQtYXdlc29tZS8KCmBgYAoKIyMgbGV0cyBmaW5kIG91dCB3aGljaCBtYW51ZmFjdHVyZXIgaGFzIHRoZSBoaWdoZXN0IGRlbGF5cwoKZmlyc3Qgd2UgbmVlZCB0byBqb2luIGZsaWdodHMgd2l0aCBwbGFuZXMKCmBgYHtyfQpmbGlnaHRfcGxhbmVzIDwtIGxlZnRfam9pbihmbGlnaHRzLCBwbGFuZXMsIGJ5ID0gInRhaWxudW0iKQoKZmxpZ2h0X3BsYW5lcyAlPiUgZ3JvdXBfYnkobWFudWZhY3R1cmVyKSAlPiUgc3VtbWFyaXNlKGRlbGF5X3Blcl9mbGlnaHQgPSBzdW0oYXJyX2RlbGF5LCBuYS5ybSA9IFRSVUUpLyBuKCksbnVtYmVyX29mX2ZsaWdodHMgPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MoZGVsYXlfcGVyX2ZsaWdodCkpCgpgYGAKCiMjIGxldHMgZmluZCBvdXQgd2hpY2ggYWlybGluZSBoYXMgdGhlIGhpZ2hlc3QgZGVsYXlzCmZpcnN0IHdlIG5lZWQgdG8gam9pbiBmbGlnaHRzIHdpdGggcGxhbmVzCgpgYGB7cn0KZmxpZ2h0X2FpcmxpbmVzIDwtIGxlZnRfam9pbihmbGlnaHRzLCBhaXJsaW5lcykKCmZsaWdodF9haXJsaW5lcyAlPiUgZ3JvdXBfYnkobmFtZSkgJT4lIHN1bW1hcmlzZShkZWxheV9wZXJfZmxpZ2h0ID0gc3VtKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKS8gbigpLG51bWJlcl9vZl9mbGlnaHRzID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKGRlbGF5X3Blcl9mbGlnaHQpKQoKYGBgCgoKCiMjIGxvbmcgYW5kIHdpZGUgZGF0YS5mcmFtZXMKCmZvciBzb21lIG9wZXJhdGlvbnMgdGhlIHRpZHkgd2lkZSBmb3JtYXQgaXMgbm90IHN1aXRhYmxlIGFzIGlucHV0IHRvIGFuIG9wZXJhdGlvbiwgdGhlbiBhICJsb25nIiB2ZXJzaW9uIG9mIHRoZSBkYXRhLmZyYW1lIGNhbiBiZSBnZW5lcmF0ZWQgdXNpbmcgdGhlICJtZWx0IiBjb21tYW5kLgpBIGZ1cnRoZXIgZXhhbXBsZSB3aWxsIGJlIHNob3duIGluICoqRXVyb3BlTGVhZ3VlVHJhbnNmZXJzLlJtZCoqIGFuZCBmdXJ0aGVyIGluZm9ybWF0aW9uIG9uIHRoZSB0b3BpYyBjYW4gYmUgZm91bmQgYXQgaHR0cDovL3NlYW5hbmRlcnNvbi5jYS8yMDEzLzEwLzE5L3Jlc2hhcGUuaHRtbCAKCmBgYHtyfQpsaWJyYXJ5KHJlc2hhcGUyKQpuYW1lcyhhaXJxdWFsaXR5KSA8LSB0b2xvd2VyKG5hbWVzKGFpcnF1YWxpdHkpKQphcW0gPC0gbWVsdChhaXJxdWFsaXR5LCBpZD1jKCJtb250aCIsICJkYXkiKSwKICB2YXJpYWJsZS5uYW1lID0gImNsaW1hdGVfdmFyaWFibGUiLCAKICB2YWx1ZS5uYW1lID0gImNsaW1hdGVfdmFsdWUiKQphaXJxdWFsaXR5CmFxbQpgYGAKCgpgYGB7cn0KKGFjYXN0X3Jlc3VsdCA8LSBhY2FzdChhcW0sIGRheSB+IG1vbnRoIH4gY2xpbWF0ZV92YXJpYWJsZSwgbmEucm0gPSBUUlVFKSkKYWNhc3QoYXFtLCBtb250aCB+IGNsaW1hdGVfdmFyaWFibGUsIG1lYW4sIG5hLnJtID0gVFJVRSkKYWNhc3RfcmVzdWx0CmFjYXN0X3Jlc3VsdFsyMiw1LF0gICMgYXJyYXlzIGFyZSBhY2Nlc3NlZCAKYGBgCgoKIyMgbGFzdCB0aGluZyB3ZSBuZWVkIGZvciBFdXJvcGVMZWFndWVUcmFuc2ZlcnMuUm1kCgpncmVwbCByZXR1cm5zIGEgbG9naWMgdmVjdG9yIGdpdmVuIGFuIGV4cHJlc3Npb24KCmBgYHtyfQpsZXR0ZXJzCmdyZXAoIlthLWNdIiwgbGV0dGVycykKZ3JlcCgiW2Etel0iLCBsZXR0ZXJzKQpncmVwbCgiW2EtY10iLCBsZXR0ZXJzKQpncmVwbCgiW2Etel0iLCBsZXR0ZXJzKQoKYGBgCgoKIyBMZXRzIGRpdmUgaW50byBzb21lIGNvZGUKCioqRXVyb3BlTGVhZ3VlVHJhbnNmZXJzLlJtZCoq